#include <cctype>
#include <string>
#include "Scaner.h"
using namespace std;

#define CRASH return false;
#define STATE(i) {state = i; break;}

#define val_token 0
#define sep_token 1
#define key_token 2
#define id_token 3
#define unknown_token 4

Scaner::Scaner(char* ifile, char* ofile) {
    valHt = new HashTable("Constants", 1000);
    sepHt = new HashTable("Separator", 60);
    keyHt = new HashTable("Keyword", 60);
    idHt = new HashTable("Identifier", 1000);
    if (!freopen(ifile, "rt", stdin))
        throw 1;
    if (ofile)
        if (!freopen(ofile, "wt", stdout))
            throw 1;
    makeSepHt();
    makeKeyHt();
    strNumber = -1;
    char s[101];
    if (!gets(s))
        throw "Empty file !!!\n";
    strNumber++;
    if (s[strlen(s) - 1] == '\n')
        s[strlen(s) - 1] = '\0';
    Buff = new string(s);
    strBegIdx = 0;
}

Scaner::~Scaner() {
    if (valHt)
        delete valHt;
    if (sepHt)
        delete sepHt;
    if (keyHt)
        delete keyHt;
    if (idHt)
        delete idHt;
    if (Buff)
        delete Buff;
}

string* Scaner::getLexeme(int& token) {
    if (strBegIdx == Buff->size())
        if (!nextString())
            return NULL;
    int cur[5];
    int prev[5];
    int states[5];
    token = -1;
    //Инициализация массивов состояний
    for (int i = 0; i < 5; i++) {
        prev[i] = cur[i] = 1;
        states[i] = 0;
    }
    // Убираем пробельные символы
    while ((*Buff)[strBegIdx] == ' ' || (*Buff)[strBegIdx] == '\t')
        strBegIdx++;
    // Пробег до конца строки + холостой ход
    // для принудительной поломки автоматов
    for (int i = strBegIdx; i <= Buff->size(); i++) {
        // Принудительная поломка автоматов
        if (i == Buff->size()) {
            for (int j = 0; j < 6; j++) {
                prev[j] = cur[j];
                cur[j] = 0;
            }
        } else {
            if (cur[0])
                cur[0] = valMac((*Buff)[i], states[0]);
            else
                prev[0] = 0;
            if (cur[1]) {
                cur[1] = sepMac((*Buff)[i], states[1]);
                if (cur[1])
                    cur[4] = 0;
            } else
                prev[1] = 0;
            if (cur[2])
                cur[2] = keyMac((*Buff)[i], states[2]);
            else
                prev[2] = 0;
            if (cur[3])
                cur[3] = idMac((*Buff)[i], states[3]);
            else
                prev[3] = 0;
            if (!(cur[0] || cur[1] || cur[2] || cur[3]))
                states[4] = 1;
            if (cur[4] && states[4])
                cur[4] = unknownMac((*Buff)[i]);
        }
        if (!cur[0] && !cur[1] && !cur[2] && !cur[3] && !cur[4]) {
            string *res = new string;
            res->append((*Buff), strBegIdx, i - strBegIdx);
            if (prev[0] && valFin(states[0])) token = val_token;
            if (prev[1] && sepFin(states[1])) token = sep_token;
            if (prev[2] && keyFin(states[2])) token = key_token;
            else if (prev[3] && idFin(states[3])) token = id_token;
            if (prev[4] && token == -1) token = unknown_token;
            if ((*Buff)[i] == 32)
                i++;
            strBegIdx = i;
            return res;
        }
    }
}

// Обслуживающие

void Scaner::makeSepHt() {
    // +-*/%&|=<>!,.:;()[]{}
    sepHt->hash("+"); sepHt->hash("++"); sepHt->hash("+=");
    sepHt->hash("-"); sepHt->hash("--"); sepHt->hash("-="); 
    sepHt->hash("*"); sepHt->hash("*=");
    sepHt->hash("/"); sepHt->hash("/=");
    sepHt->hash("%"); sepHt->hash("%=");
    sepHt->hash("&"); sepHt->hash("&&");
    sepHt->hash("|"); sepHt->hash("||");
    sepHt->hash("="); sepHt->hash("==");
    sepHt->hash("<"); sepHt->hash("<<"); sepHt->hash("<=");
    sepHt->hash(">"); sepHt->hash(">>"); sepHt->hash(">=");
    sepHt->hash("!"); sepHt->hash("!=");
    sepHt->hash(",");
    sepHt->hash(".");
    sepHt->hash(":"); sepHt->hash("::");
    sepHt->hash(";");
    sepHt->hash("(");
    sepHt->hash(")");
    sepHt->hash("[");
    sepHt->hash("]");
    sepHt->hash("{");
    sepHt->hash("}");
    
    
}

void Scaner::makeKeyHt() {
    keyHt->hash("int"); keyHt->hash("float"); keyHt->hash("double");
    keyHt->hash("char"); keyHt->hash("bool"); keyHt->hash("long"); 
    keyHt->hash("short"); keyHt->hash("break"); keyHt->hash("for");
    keyHt->hash("while"); keyHt->hash("sizeof"); keyHt->hash("do");
    keyHt->hash("continue"); keyHt->hash("if"); keyHt->hash("else");
    keyHt->hash("signed"); keyHt->hash("unsigned"); keyHt->hash("struct");
    keyHt->hash("class"); keyHt->hash("public"); keyHt->hash("private");
    keyHt->hash("virtual");
}

bool Scaner::nextString() {
    char s[101];
    do{
        if (!gets(s))
            return false;
    }
    while(s[0] == '\n');
    strNumber++;
    if (s[strlen(s) - 1] == 13)
        s[strlen(s) - 1] = '\0';
    delete Buff;
    Buff = new string(s);
    strBegIdx = 0;
    return true;
}
//

// Автоматы

bool Scaner::valMac(char c, int &state) {
    switch (state) {
        case 0:
            if (isdigit(c)) STATE(1)
            if (c == '.') STATE(10)
            CRASH
        case 1:
            if (c == '.') STATE(2)
            if (isdigit(c)) STATE(7)
            CRASH
        case 2:
            if (isdigit(c)) STATE(3)
            CRASH
        case 3:
            if (isdigit(c)) break;
            if (c == 'e') STATE(4)
            CRASH
        case 4:
            if (isdigit(c)) STATE(6)
            if (c == '+' || c == '-') STATE(5)
            CRASH
        case 5:
            if (isdigit(c)) STATE(6)
            CRASH
        case 6:
            if (isdigit(c)) break;
            CRASH
        case 7:
            if (isdigit(c)) break;
            if (c == '.') STATE(8)
            CRASH
        case 8:
            if (isdigit(c)) STATE(9)
            CRASH
        case 9:
            if (isdigit(c)) break;
            CRASH
        case 10:
            if (isdigit(c)) STATE(11)
            CRASH
        case 11:
            if (isdigit(c)) break;
            CRASH
    }
    return true;
}

bool Scaner::valFin(int &s) {
    if (s == 0 || s == 4 || s == 10)
        return false;
    return true;
}

bool Scaner::sepMac(char c, int& state){
    string un(".,;~()[]{}");
    string nd("*/%=!");
    switch(state){
        case 0:
            if(un.find_first_not_of(c,0) != un.npos) STATE(1)
            if(nd.find_first_not_of(c,0) != nd.npos) STATE(2)
            if(c == '&') STATE(4)
            if(c == '|') STATE(6)
            if(c == '+') STATE(8)
            if(c == '-') STATE(10)
            if(c == '<') STATE(12)
            if(c == '>') STATE(14)
            CRASH
        case 1:
            CRASH
        case 2:
            if(c == '=') STATE(3)
            CRASH
        case 3:
            CRASH
        case 4:
            if(c == '&') STATE(5)
            CRASH
        case 5:
            CRASH
        case 6:
            if(c == '|') STATE(7)
            CRASH
        case 7:
            CRASH
        case 8:
            if(c == '+') STATE(9)
            if(c == '=') STATE(3)
            CRASH
        case 9:
            CRASH
        case 10:
            if(c == '-') STATE(11)
            if(c == '=') STATE(3)
            CRASH
        case 11:
            CRASH
        case 12:
            if(c == '<') STATE(13)
            if(c == '=') STATE(3)
            CRASH
        case 13:
            CRASH
        case 14:
            if(c == '>') STATE(15)
            if(c == '=') STATE(3)
            CRASH
        case 15:
            CRASH            
    }
    return true;
}

bool Scaner::sepFin(int& state){
    if(!state)
        return false;
    return true;
}

bool Scaner::keyMac(char c, int& state){
    switch(state){
        case 0:
            if(c == 'i') STATE(1)
            if(c == 'f') STATE(5)
            if(c == 'd') STATE(12)
            if(c == 'c') STATE(18)
            if(c == 'b') STATE(33)
            if(c == 's') STATE(41)
            if(c == 'u') STATE(60)
            if(c == 'p') STATE(64)
            if(c == 'l') STATE(76)
            if(c == 'e') STATE(80)
            if(c == 'w') STATE(84)
            if(c == 'v') STATE(89)
            CRASH
        case 1:
            if(c == 'f') STATE(2)
            if(c == 'n') STATE(3)
            CRASH
        case 2:
            CRASH
        case 3:
            if(c == 't') STATE(4)
            CRASH
        case 4:
            CRASH
        case 5:
            if(c == 'o') STATE(6)
            if(c == 'l') STATE(8)
            CRASH
        case 6:
            if(c == 'r') STATE(7)
            CRASH
        case 7:
            CRASH
        case 8:
            if(c == 'o') STATE(9)
            CRASH
        case 9:
            if(c == 'a') STATE(10)
            CRASH
        case 10:
            if(c == 't') STATE(11)
            CRASH
        case 11:
            CRASH
        case 12:
            if(c == 'o') STATE(13)
            CRASH
        case 13:
            if(c == 'u') STATE(13)
            CRASH
        case 14:
            if(c == 'b') STATE(13)
            CRASH
        case 15:
            if(c == 'l') STATE(13)
            CRASH
        case 16:
            if(c == 'e') STATE(13)
            CRASH
        case 17:
            CRASH
        case 18:
            if(c == 'h') STATE(19)
            if(c == 'l') STATE(22)
            if(c == 'o') STATE(26)
            CRASH
        case 19:
            if(c == 'a') STATE(20)
            CRASH
        case 20:
            if(c == 'r') STATE(21)
            CRASH
        case 21:
            CRASH
        case 22:
            if(c == 'a') STATE(23)
            CRASH
        case 23:
            if(c == 's') STATE(24)
            CRASH
        case 24:
            if(c == 's') STATE(25)
            CRASH
        case 25:
            CRASH
        case 26:
            if(c == 'n') STATE(27)
            CRASH
        case 27:
            if(c == 't') STATE(28)
            CRASH
        case 28:
            if(c == 'i') STATE(29)
            CRASH
        case 29:
            if(c == 'n') STATE(30)
            CRASH
        case 30:
            if(c == 'u') STATE(31)
            CRASH
        case 31:
            if(c == 'e') STATE(32)
            CRASH
        case 32:
            CRASH
        case 33:
            if(c == 'o') STATE(34)
            if(c == 'r') STATE(37)
            CRASH
        case 34:
            if(c == 'o') STATE(35)
            CRASH
        case 35:
            if(c == 'l') STATE(36)
            CRASH
        case 36:
            CRASH
        case 37:
            if(c == 'e') STATE(38)
            CRASH
        case 38:
            if(c == 'a') STATE(39)
            CRASH
        case 39:
            if(c == 'k') STATE(40)
            CRASH
        case 40:
            CRASH
        case 41:
            if(c == 'h') STATE(42)
            if(c == 't') STATE(46)
            if(c == 'i') STATE(51)
            CRASH
        case 42:
            if(c == 'o') STATE(43)
            CRASH
        case 43:
            if(c == 'r') STATE(44)
            CRASH
        case 44:
            if(c == 't') STATE(45)
            CRASH
        case 45:
            CRASH
        case 46:
            if(c == 'r') STATE(47)
            CRASH
        case 47:
            if(c == 'u') STATE(48)
            CRASH
        case 48:
            if(c == 'c') STATE(49)
            CRASH
        case 49:
            if(c == 't') STATE(50)
            CRASH
        case 50:
            CRASH
        case 51:
            if(c == 'z') STATE(52)
            if(c == 'g') STATE(56)
            CRASH
        case 52:
            if(c == 'e') STATE(53)
            CRASH
        case 53:
            if(c == 'o') STATE(54)
            CRASH
        case 54:
            if(c == 'f') STATE(55)
            CRASH
        case 55:
            CRASH
        case 56:
            if(c == 'g') STATE(57)
            CRASH
        case 57:
            if(c == 'n') STATE(58)
            CRASH
        case 58:
            if(c == 'e') STATE(59)
            CRASH
        case 59:
            CRASH
        case 60:
            if(c == 'n') STATE(61)
            CRASH
        case 61:
            if(c == 's') STATE(62)
            CRASH
        case 62:
            if(c == 'i') STATE(63)
            CRASH
        case 63:
            if(c == 'g') STATE(57)
            CRASH
        case 64:
            if(c == 'u') STATE(65)
            if(c == 'r') STATE(70)
            CRASH
        case 65:
            if(c == 'b') STATE(66)
            CRASH
        case 66:
            if(c == 'l') STATE(67)
            CRASH
        case 67:
            if(c == 'i') STATE(68)
            CRASH
        case 68:
            if(c == 'c') STATE(69)
            CRASH
        case 69:
            CRASH
        case 70:
            if(c == 'i') STATE(71)
            CRASH
        case 71:
            if(c == 'v') STATE(72)
            CRASH
        case 72:
            if(c == 'a') STATE(73)
            CRASH
        case 73:
            if(c == 't') STATE(74)
            CRASH
        case 74:
            if(c == 'e') STATE(75)
            CRASH
        case 75:
            CRASH
        case 76:
            if(c == 'o') STATE(77)
            CRASH
        case 77:
            if(c == 'n') STATE(78)
            CRASH
        case 78:
            if(c == 'g') STATE(79)
            CRASH
        case 79:
            CRASH
        case 80:
            if(c == 'l') STATE(81)
            CRASH
        case 81:
            if(c == 's') STATE(82)
            CRASH
        case 82:
            if(c == 'e') STATE(83)
            CRASH
        case 83:
            CRASH
        case 84:
            if(c == 'h') STATE(85)
            CRASH
        case 85:
            if(c == 'i') STATE(86)
            CRASH
        case 86:
            if(c == 'l') STATE(87)
            CRASH
        case 87:
            if(c == 'e') STATE(88)
            CRASH
        case 88:
            CRASH
        case 89:
            if(c == 'i') STATE(90)
            CRASH
        case 90:
            if(c == 'r') STATE(91)
            CRASH
        case 91:
            if(c == 't') STATE(92)
            CRASH
        case 92:
            if(c == 'u') STATE(93)
            CRASH
        case 93:
            if(c == 'a') STATE(94)
            CRASH
        case 94:
            if(c == 'l') STATE(95)
            CRASH
        case 95:
            CRASH
    }
    return true;
}

bool Scaner::keyFin(int &s){
    if(s == 4 || s == 7 || s == 11 || s == 17 || s == 21 || s == 25 || 
       s == 32 || s == 36 || s == 40 || s == 45 || s == 50 || s == 55 || 
       s == 59 || s == 69 || s == 75 || s == 79 || s == 83 || s == 88 || 
       s == 95)
        return true;
    return false;
}

bool Scaner::idMac(char c, int& state){
    if(!state && isalpha(c)){
        state = 1;
        return true;
    }
    if(state && (isalpha(c) || isdigit(c)))
        return true;
    else
        return false;
}

bool Scaner::idFin(int& state){
    if(state)
        return true;
    return false;
}

bool Scaner::unknownMac(char c) {
    string sep("+-*/%&|=<>!,.:;()[]{}");
    if (sep.find_first_of(c,0) != sep.npos)
        return false;
    return true;
}
//